import { EPSG3857 } from "./crs/CRS.EPSG3857";

import { EPSG4326 } from "./crs/CRS.EPSG4326";

import { WMS } from "./geo/WMS";
import { Projection } from "./geo/Projection";
import { toLatLng } from "./geometry/LatLng";
import { toPoint, Point } from "./geometry/Point";
import { toBounds } from "./geometry/Bounds";

import { svgRuler } from "./util/svg";
import { coorsOffset } from "./util/CoorsOffset";

import { LatLngBounds, toLatLngBounds } from "./geometry/LatLngBounds";

export default class engine {
  parentNode;
  width;
  height;
  canvas;
  ctx;
  option;
  recordMovePoint = toPoint(0, 0);
  recordMovePointWMS = toPoint(0, 0);
  mapCenterPos;
  projection;
  projectionWMS;
  measureDrawMode = "stop"; // 这个变量仅用于开始绘制，默认是stop, 还有状态是start
  crspro = EPSG3857();
  crs = "EPSG3857"; // EPSG4326,
  zoomEnable = true; //是否缩放
  dragEnable = true; //是否拖动
  animate = true; // 是否支持动画
  eventCallback = {}; // 这个是事件的回调，我用了defineProperty
  constructor(id, option) {
    this.initContainer(id);
    this.canvas.addEventListener("mousemove", this.mouseMove.bind(this));
    this.canvas.addEventListener("mousewheel", this.mouseWheel.bind(this));
    this.canvas.addEventListener("mousedown", this.mouseDown.bind(this));
    this.canvas.addEventListener("mouseup", this.onmouseUp.bind(this));
    this.canvas.addEventListener("click", this.click.bind(this));
    this.canvas.addEventListener("dblclick", this.dblclick.bind(this));

    // 设置画布大小
    this.width = this.canvas.width;
    this.height = this.canvas.height;
    // 获取绘图上下文
    this.ctx = this.canvas.getContext("2d");
    this.option = option;
    this.initToolbar();
    //  const tempCen = coorsOffset.gcj02towgs84(option.center[0], option.center[1])

    //   debugger
    //   this.option.center = [tempCen.lng, tempCen.lat]
    const { center, zoom } = this.option;
    this.zoom = zoom;
    // this.mapCenterPos = tempCen;
    this.mapCenterPos = toLatLng(center[0], center[1]);
    this.zoomEnable =
      typeof this.option.zoomEnable === "undefined"
        ? this.zoomEnable
        : this.option.zoomEnable;
    this.dragEnable =
      typeof this.option.dragEnable === "undefined"
        ? this.dragEnable
        : this.option.dragEnable;
    this.animate =
      typeof this.option.animate === "undefined"
        ? this.animate
        : this.option.animate;

    this.option.measureLayer =
      typeof this.option.measureLayer === "undefined"
        ? []
        : this.option.measureLayer;

    // this.projection = new WMS({
    //   lng: this.mapCenterPos.lng,
    //   lat: this.mapCenterPos.lat,
    //   zoom: this.zoom,
    //   ctx: this.ctx,
    //   canvas: this.canvas,
    //   drawLayer: this.option.drawLayer || [],
    //   animate: this.option.animate,
    // });
    // 修改1
    this.crs = this.option.crs || "EPSG3857";
    if (this.crs === "EPSG4326") {
      this.crspro = EPSG4326();
    }
    const baseLayer = this.option.baseLayer || [];
    if (baseLayer.length === 0) {
      baseLayer.push({
        type: "WMTS",
        url: "https://webrd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
      });
    }
    this.projection = new Projection({
      lng: this.mapCenterPos.lng,
      lat: this.mapCenterPos.lat,
      zoom: this.zoom,
      ctx: this.ctx,
      crs: this.crs,
      crspro: this.crspro,
      canvas: this.canvas,
      baseLayer,
      drawLayer: this.option.drawLayer || [],
      measureLayer: this.option.measureLayer || [],
      animate: this.animate,
      measureMode: this.option.measureMode || null,
    });
    // return this
  }

  initContainer(id) {
    const container = (this.parentNode = this.getId(id));
    if (!container) {
      throw new Error("Map container not found.");
    }

    this.parentNode.style.position = "fixed";

    this.canvas = document.createElement("canvas");
    this.canvas.width = this.parentNode.clientWidth;
    this.canvas.height = this.parentNode.clientHeight;
    this.parentNode.appendChild(this.canvas);
  }
  getId(id) {
    return typeof id === "string" ? document.getElementById(id) : id;
  }

  initToolbar() {
    if (!this.option?.toolbar) return;

    const el = document.createElement("div");
    el.style.position = "absolute";
    el.style.right = "20px";
    el.style.top = "20px";
    el.style.display = "flex";

    this.parentNode.appendChild(el);

    this.option.toolbar.forEach((item) => {
      const obj = {
        polyline: "测距",
        polygon: "测面",
      };
      const itemDom = document.createElement("div");
      itemDom.style.background = "#ffffffcc";
      itemDom.style.padding = "5px 10px";
      itemDom.style.cursor = "pointer";
      itemDom.style.fontSize = "12px";
      itemDom.innerHTML = obj[item.type];
      itemDom.style.borderRadius = "5px";
      itemDom.style.margin = "2px";
      itemDom.onclick = () => {
        if (this.option.measureMode) {
          this.option.measureMode = null;
        } else {
          this.option.measureMode = item.type;
        }
      };
      el.appendChild(itemDom);
    });
  }

  callback(e, ee) {
    ee(e);
  }

  on(type, callback) {
    let temp = {};
    const that = this;
    Object.defineProperty(this.eventCallback, "mouse", {
      enumerable: true,
      get: function (e) {
        return temp;
      },
      set: function (newVal) {
        temp = newVal;
        that.eventCallback.checkInfos = that.projection.checkInfos; // 这个是事件的回调，我用了defineProperty
        callback(that.eventCallback);
      },
    });
  }

  // polyline polygon
  setMeasureMode(mode = polygon) {
    this.option.measureMode = mode;
  }

  flyTo(lng, lat, zoom) {
    this.projection.scaleMap({
      lng,
      lat,
      zoom: zoom ? zoom : this.zoom,
    });
  }
  // 飞面
  // option {padding: [60, 60]} 由于没有写放大缩小，padding只有当改变地图层级的时候才有效果
  flyRange(coordinates, option) {
    const boundsCenterZoom = this.getBoundsCenterZoom(
      toLatLngBounds(coordinates),
      option
    );
    this.zoom = boundsCenterZoom.zoom;
    this.option.center = [
      boundsCenterZoom.center.lng,
      boundsCenterZoom.center.lat
    ];
    this.projection.scaleMap({
      lng: boundsCenterZoom.center.lng,
      lat: boundsCenterZoom.center.lat,
      zoom: boundsCenterZoom.zoom,
    });
  }

  getBoundsCenterZoom(bounds, options) {
    options = options || {};
    bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
    const paddingTL = toPoint(
        options.paddingTopLeft || options.padding || [0, 0]
      ),
      paddingBR = toPoint(
        options.paddingBottomRight || options.padding || [0, 0]
      );

    let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));

    zoom =
      typeof options.maxZoom === "number"
        ? Math.min(options.maxZoom, zoom)
        : zoom;

    if (zoom === Infinity) {
      return {
        center: bounds.getCenter(),
        zoom,
      };
    }

    const paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
      swPoint = this.project(bounds.getSouthWest(), zoom),
      nePoint = this.project(bounds.getNorthEast(), zoom),
      center = this.unproject(
        swPoint.add(nePoint).divideBy(2).add(paddingOffset),
        zoom
      );

    return {
      center,
      zoom,
    };
  }
  project(latlng, zoom) {
    zoom = zoom === undefined ? this._zoom : zoom;
    return this.crspro.lngLatToPoint(toLatLng(latlng), zoom);
  }
  unproject(point, zoom) {
		zoom = zoom === undefined ? this._zoom : zoom;
		return this.crspro.pointToLatLng(toPoint(point), zoom);
	}

  getSize() {
		return new Point(
      this.canvas.clientWidth || 0,
      this.canvas.clientHeight || 0
    );
	}
  getBoundsZoom(bounds, inside, padding) {
    bounds = toLatLngBounds(bounds);
    padding = toPoint(padding || [0, 0]);

    let zoom = this.zoom || 0;
    const min = 0, // this.getMinZoom()
      max = 18, //this.getMaxZoom()
      nw = bounds.getNorthWest(),
      se = bounds.getSouthEast(),
      size = this.getSize().subtract(padding),
      boundsSize = toBounds(
        this.project(se, zoom),
        this.project(nw, zoom)
      ).getSize(),
      snap = this.options?.zoomSnap || 1,
      scalex = size.x / boundsSize.x,
      scaley = size.y / boundsSize.y,
      scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
    zoom = this.getScaleZoom(scale, zoom);
    console.log(scale, zoom, "sss");

    if (snap) {
      zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
      zoom = inside
        ? Math.ceil(zoom / snap) * snap
        : Math.floor(zoom / snap) * snap;
    }
    return Math.max(min, Math.min(max, zoom));
  }

  getScaleZoom(scale, fromZoom) {
		fromZoom = fromZoom === undefined ? this.zoom : fromZoom;
		const zoom = this.crspro.zoom(scale * this.crspro.scale(fromZoom));
		return isNaN(zoom) ? Infinity : zoom;
	}

  dblclick(e) {
    this.measureDrawMode = "stop";

    this.option.measureMode = undefined;
    document.getElementById("measureTip") &&
      this.parentNode.removeChild(document.getElementById("measureTip"));

    // 修复单击多添加的点
    const temp = this.option.measureLayer;
    let tempCoords = temp[temp.length - 1].coordinates;
    tempCoords = tempCoords.splice(tempCoords.length - 1, 1);
    // 给这个数据一个ID，后边方便删除
    temp[temp.length - 1].id = "measure" + Date.now();
  }

  click(e) {
    this.eventCallback.mouse = e;
    if (this.option.measureMode) {
      // this.measureDrawMode = "start";
      const nowLnglat = this.getNowLngLat(e);
      if (this.measureDrawMode === "start") {
        const temp = this.option.measureLayer;
        const tempCoords = temp[temp.length - 1].coordinates;

        // let obj2 = Object.assign([], tempCoords);

        if (tempCoords.length < 2) {
          tempCoords.push([nowLnglat?.lng, nowLnglat?.lat]);
        } else {
          const lastTempCoords = tempCoords[tempCoords.length - 2];
          if (
            lastTempCoords[0] !== nowLnglat.lng ||
            lastTempCoords[1] !== nowLnglat.lat
          ) {
            tempCoords.push([nowLnglat?.lng, nowLnglat?.lat]);
          }
        }
      } else if (this.measureDrawMode === "stop") {
        this.measureDrawMode = "start";
        this.option.measureLayer.push({
          type: this.option.measureMode,
          coordinates: [
            [nowLnglat?.lng, nowLnglat?.lat],
            [nowLnglat?.lng, nowLnglat?.lat],
          ],
        });
      }
    }
  }

  mouseDown(e) {
    const downX = e.offsetX;
    const downY = e.offsetY;
    //  WMS 每次开始移动要重新计算偏移 修改2
    // this.recordMovePointWMS = toPoint(0, 0);
    // const kxWMS = this.recordMovePointWMS.x;
    // const kyWMS = this.recordMovePointWMS.y;

    const kx = this.recordMovePoint.x;
    const ky = this.recordMovePoint.y;

    // this.canvas.onmousemove = (ev) => {
    //   const moveX = ev.offsetX;
    //   const moveY = ev.offsetY;

    //   this.recordMovePoint.x = kx + (moveX - downX);
    //   this.recordMovePoint.y = ky + (moveY - downY);

    //   let mx =
    //     this.recordMovePoint.x * this.projection.getResolution(this.zoom);
    //   let my =
    //     this.recordMovePoint.y * this.projection.getResolution(this.zoom);
    //   // 把当前中心点经纬度转成3857坐标

    //   let tempPoint = this.crspro.projection.project(
    //     toLatLng(this.option.center[0], this.option.center[1])
    //   );

    //   this.mapCenterPos = this.crspro.projection.unproject(
    //     toPoint(tempPoint.x - mx, my + tempPoint.y)
    //   );

    //   if (this.dragEnable) {
    //     this.projection.moveTo(this.recordMovePoint);
    //     // WMS 最终才移动一次， WMTS 是移动的过程中要随时刷新 WMS要隐藏这里  修改3
    //     this.projection.updata(
    //       this.mapCenterPos.lng,
    //       this.mapCenterPos.lat,
    //       this.option.zoom
    //     );
    //   }

    //   // 设置当鼠标移动小于边界的10像素内，移除移动事件
    //   if (
    //     moveX > this.canvas.width - 10 ||
    //     moveX < 10 ||
    //     moveY < 10 ||
    //     moveY > this.canvas.height - 10
    //   ) {
    //     this.canvas.onmousemove = null;
    //     this.canvas.onmouseup = null;
    //   }

    //   // 鼠标抬起移除移动事件
    //   this.canvas.onmouseup = (en) => {
    //     // WMS 最终才移动一次， WMTS 是移动的过程中要随时刷新 修改4
    //     // this.option.center = [this.mapCenterPos.lng, this.mapCenterPos.lat];
    //     // this.projection.updata(
    //     //   this.mapCenterPos.lng,
    //     //   this.mapCenterPos.lat,
    //     //   this.option.zoom
    //     // );
    //     this.canvas.onmousemove = null;
    //     this.canvas.onmouseup = null;
    //   };
    // };

    this.canvas.onmousemove = (ev) => {
      const moveX = ev.offsetX;
      const moveY = ev.offsetY;

      this.recordMovePoint.x = kx + (moveX - downX);
      this.recordMovePoint.y = ky + (moveY - downY);

      let mx = this.recordMovePoint.x;
      let my = this.recordMovePoint.y;
      // 把当前中心点经纬度转成3857坐标

      let tempPoint = this.crspro
        .lngLatToPoint(
          toLatLng(this.option.center[0], this.option.center[1]),
          this.zoom
        )
        .floor();
      this.mapCenterPos = this.crspro.pointToLatLng(
        toPoint(tempPoint.x - mx, tempPoint.y - my),
        this.zoom
      );

      if (this.dragEnable) {
        this.projection.moveTo(this.recordMovePoint);

        // WMS 最终才移动一次， WMTS 是移动的过程中要随时刷新 WMS要隐藏这里  修改3
        this.projection.updata(
          this.mapCenterPos.lng,
          this.mapCenterPos.lat,
          this.option.zoom
        );
      }

      // 设置当鼠标移动小于边界的10像素内，移除移动事件
      if (
        moveX > this.canvas.width - 10 ||
        moveX < 10 ||
        moveY < 10 ||
        moveY > this.canvas.height - 10
      ) {
        this.canvas.onmousemove = null;
        this.canvas.onmouseup = null;
      }

      // 鼠标抬起移除移动事件
      this.canvas.onmouseup = (en) => {
        // WMS 最终才移动一次， WMTS 是移动的过程中要随时刷新 修改4
        // this.option.center = [this.mapCenterPos.lng, this.mapCenterPos.lat];
        // this.projection.updata(
        //   this.mapCenterPos.lng,
        //   this.mapCenterPos.lat,
        //   this.option.zoom
        // );
        this.canvas.onmousemove = null;
        this.canvas.onmouseup = null;
      };
    };
  }

  onmouseUp() {
    this.canvas.onmousemove = null;
    this.canvas.onmouseup = null;
  }
  /**
   * 滚轮缩放倍数
   */
  mouseWheel(e) {
    if (!this.zoomEnable) return;
    if (e.wheelDelta > 0) {
      if (this.zoom < 16) {
        // this.zoom += 0.5;
        this.zoom++;
      }
    } else {
      if (this.zoom > 3) {
        // this.zoom -= 0.5;
        this.zoom--;
      }
    }
    if (this.isInteger(this.zoom)) {
      // 缩放有两个关键点，一个是中心点，中心点应该设置在鼠标当前处于的位置，一个是缩放倍数
      this.recordMovePoint = toPoint(0, 0);
      this.option.center = [this.mapCenterPos.lng, this.mapCenterPos.lat];
      this.projection.scaleMap({
        lng: this.mapCenterPos.lng,
        lat: this.mapCenterPos.lat,
        zoom: this.zoom,
      });

      // this.projection = new Projection({
      //   lng: this.mapCenterPos.lng,
      //   lat: this.mapCenterPos.lat,
      //   zoom: this.zoom,
      //   ctx: this.ctx,
      //   canvas: this.canvas,
      // });
    }
  }
  measureFn(e) {
    if (this.option.measureMode) {
      this.canvas.style.cursor =
        this.option.measureMode === "polyline"
          ? `url('ruler.png') 6 9, auto`
          : `url('area.png') 6 5, auto`;
      let el;
      if (document.getElementById("measureTip")) {
        el = document.getElementById("measureTip");
      } else {
        el = document.createElement("div");
        el.id = "measureTip";
        this.parentNode.appendChild(el);
      }
      el.style.position = "absolute";

      let a = 0;
      if (
        this.option.measureLayer.length > 0 &&
        this.measureDrawMode === "start"
      ) {
        this.option.measureLayer[
          this.option.measureLayer.length - 1
        ].coordinates.forEach((it, index, count) => {
          if (index > 0) {
            a += this.crspro.distance(
              toLatLng(it[0], it[1]),
              toLatLng(count[index - 1][0], count[index - 1][1])
            );
          }
        });
      }

      if (a > 1000) {
        a = (a / 1000).toFixed(1) + "千米";
      } else {
        a = parseInt(a) + "米";
      }

      if (this.option.measureMode === "polyline") {
        el.innerHTML =
          this.measureDrawMode === "stop"
            ? "单击确定起点"
            : "总长：" + a + "<br/>单击确定地点，双击结束";
      } else {
        el.innerHTML =
          this.measureDrawMode === "stop"
            ? "单击确定起点"
            : "单击确定地点，双击结束";
      }
      el.style.left = e.clientX + 0 + "px";
      el.style.top = e.clientY + 3 + "px";
      el.style.border = "1px solid #ff0000";
      el.style.background = "#fff";
      el.style.fontSize = "12px";
      el.style.padding = "2px 5px";
      el.style.color = "#000";
      el.style.whiteSpace = "nowrap";

      const nowLnglat = this.getNowLngLat(e);
      if (this.measureDrawMode === "start") {
        this.option.measureLayer[
          this.option.measureLayer.length - 1
        ].coordinates[
          this.option.measureLayer[this.option.measureLayer.length - 1]
            .coordinates.length - 1
        ] = [nowLnglat?.lng, nowLnglat?.lat];
      }
    } else {
      this.canvas.style.cursor = "default";
    }
  }

  mouseMove(e) {
    this.measureFn(e);
    const nowLnglat = this.getNowLngLat(e);
    this.projection.mouseMove(toPoint(e.offsetX, e.offsetY), nowLnglat);
  }

  getNowLngLat(e) {
    const point = toPoint(e.offsetX, e.offsetY)
      .subtract(toPoint(this.canvas.width / 2, this.canvas.height / 2))
      .subtract(this.recordMovePoint);
    let tempPoint = this.crspro
      .lngLatToPoint(
        // this.mapCenterPos,
        toLatLng(this.option.center[0], this.option.center[1]),
        this.zoom
      )
      .floor();
    return this.crspro.pointToLatLng(tempPoint.add(point), this.zoom);
  }

  isInteger(obj) {
    return typeof obj === "number" && obj % 1 === 0;
  }
}
